/*-
 * Copyright (c) 2009 Jonathan Anderson
 * All rights reserved.
 *
 * WARNING: THIS IS EXPERIMENTAL SECURITY SOFTWARE THAT MUST NOT BE RELIED
 * ON IN PRODUCTION SYSTEMS.  IT WILL BREAK YOUR SOFTWARE IN NEW AND
 * UNEXPECTED WAYS.
 * 
 * This software was developed at the University of Cambridge Computer
 * Laboratory with support from a grant from Google, Inc. 
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <QtCore/QString>

#include <QtDBus/QDBusConnection>
#include <QtDBus/QDBusMessage>

#include <iostream>

#include <errno.h>

#include "DBusClient.h"

using namespace std;
using namespace userAngel;


DBusClient::DBusClient(ua_powerbox_ui_t ui, WId parentWindow) throw(DBusException)
	: dbus(QDBusConnection::sessionBus())
{
	if(!dbus.isConnected())
		throw DBusException("Not connected to session D-Bus");

	path = "/PowerboxServer";
	interface = "";

	setUI(ui, parentWindow);
}


void DBusClient::setUI(ua_powerbox_ui_t ui, WId parentWindow)
{
	switch(ui)
	{
		case UA_DUMMY:   serviceName = "org.freebsd.capsicum.dummy"; break;
		case UA_QT:      serviceName = "org.freebsd.capsicum.qt";    break;
		case UA_KDE:     serviceName = "org.freebsd.capsicum.kde";   break;
		default:
			throw UnknownUIException(ui);
	}

	this->parent = parentWindow;
}


bool DBusClient::confirmOverwrite(QString filename) throw(DBusException)
{
	QList<QVariant> arguments;
	arguments << filename;
	arguments << (long long) parent;

	QDBusMessage method = remoteMethod("confirmOverwrite");
	method.setArguments(arguments);

	static const int TIMEOUT = 5 * 60 * 1000;  // timeout after 5 mins

	QDBusMessage reply = dbus.call(method, QDBus::Block, TIMEOUT);
	if(reply.type() == QDBusMessage::ErrorMessage)
		if(reply.errorName() == "org.freedesktop.DBus.Error.NoReply")
			throw DBusException("DBus timed out");

		else
			throw DBusException(reply.errorName() + " - " + reply.errorMessage());

	QList<QVariant> replyArgs = reply.arguments();
	if(replyArgs.size() != 1)
		throw DBusException(
			QString("Received %1 response args, not 1").arg(replyArgs.size()));


	if(replyArgs.at(0).type() != QVariant::Bool)
		throw DBusException(
			QString("Received %1 response, not Bool").arg(
				replyArgs.at(0).typeName()));

	return replyArgs.at(0).toBool();
}


QStringList DBusClient::openFile(ua_powerbox_op_t operation, QString title,
                           QString startPath, QString filter,
                           bool allowMultipleSelect)
	throw(DBusException)
{
	QList<QVariant> arguments;
	arguments << operation;
	arguments << title;
	arguments << (long long) parent;
	arguments << startPath;
	arguments << filter;
	arguments << allowMultipleSelect;

	QDBusMessage method = remoteMethod("fileDialog");
	method.setArguments(arguments);

	static const int TIMEOUT = 5 * 60 * 1000;  // timeout after 5 mins

	QDBusMessage reply = dbus.call(method, QDBus::Block, TIMEOUT);
	if(reply.type() == QDBusMessage::ErrorMessage)
		if(reply.errorName() == "org.freedesktop.DBus.Error.NoReply")
			throw DBusException("DBus timed out");

		else
			throw DBusException(reply.errorName() + " - " + reply.errorMessage());


	QList<QVariant> replyArgs = reply.arguments();
	if(replyArgs.size() != 1)
		throw DBusException(
			QString("Received %1 response args, not 1").arg(replyArgs.size()));


	if(replyArgs.at(0).type() != QVariant::StringList)
		throw DBusException(
			QString("Received %1 response, not StringList").arg(
				replyArgs.at(0).typeName()));


	return replyArgs.at(0).toStringList();
}


QDBusMessage DBusClient::remoteMethod(QString name)
{
	return QDBusMessage::createMethodCall(serviceName, path, interface, name);
}


DBusException::DBusException(QString message)
	: Exception(message)
{
}


UnknownUIException::UnknownUIException(ua_powerbox_ui_t ui)
	: DBusException(QString("Unknown UI %1").arg(ui))
{
}

